﻿// (c) Copyright Damian Mehers http://damianblog.com/
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace MceFM.Last.Inject {
  public class Injectee {
    private static readonly Queue<string> newPages = new Queue<string>();

    static Injectee() {
      try {
        Assembly ehshell = Assembly.Load("ehshell");

        Type pageBasedUCPServiceType = ehshell.GetType("ServiceBus.UIFramework.PageBasedUCPService");

        const BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public |
                                          BindingFlags.GetProperty;
        object[] args = new object[0];
        object defaultUCP = pageBasedUCPServiceType.InvokeMember("DefaultUCP", bindingFlags, null, null, args);

        EventInfo pageChangedEventInfo = pageBasedUCPServiceType.GetEvent("PageChanged");
        Type pageChangedDelegate = pageChangedEventInfo.EventHandlerType;

        Type[] pageChangedDelegateParameters = new [] {ehshell.GetType("ServiceBus.UIFramework.Page")};
        MethodInfo newPageHandlerMethodInfo = typeof(Injectee).GetMethod("NewPageHandler");

        // Need a dynamic method because we can't create a method that has the Page type as a parameter
        DynamicMethod dynamicHandler = new DynamicMethod("", null, pageChangedDelegateParameters, typeof(Injectee));
        ILGenerator ilgen = dynamicHandler.GetILGenerator();
        ilgen.Emit(OpCodes.Nop);
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Call, newPageHandlerMethodInfo);
        ilgen.Emit(OpCodes.Nop);
        ilgen.Emit(OpCodes.Ret);

        if(pageChangedDelegate != null) {
          Delegate dEmitted = dynamicHandler.CreateDelegate(pageChangedDelegate);
          pageChangedEventInfo.GetAddMethod().Invoke(defaultUCP, new object[] {dEmitted});
        }
      } catch(Exception ex) {
        Trace.TraceError("Unexpected error in Injectee initializer: {0}", ex);
        Trace.TraceError(ex.StackTrace);
      }
    }

    private static bool notifierActive;
    
    public static void NewPageHandler(object page) {
      Trace.TraceInformation("New Page: " + page);
      lock(newPages) {
        if(!notifierActive) {
          Thread thread = new Thread(NewPageNotifier) {IsBackground = true};
          thread.Start();
          notifierActive = true;
        }
        newPages.Enqueue(page.GetType().FullName);
        Trace.TraceInformation("Injectee adding page {0} to the queue", page.GetType().FullName );
        Monitor.Pulse(newPages);
      }
    }

    private static void NewPageNotifier() {
      while(true) {
        string page;

        lock(newPages) {
          while(newPages.Count == 0) {
            Monitor.Wait(newPages);
          }
          page = newPages.Dequeue();
        }

        try {
          WebClient webClient = new WebClient();

          webClient.QueryString[Server.PAGE_QUERY_STRING] = page;
          webClient.DownloadString(Util.LocalBaseUrl + Server.MCE_PAGE_CHANGED_ACTION);
        } catch (Exception ex) {
          Trace.TraceError("Error while sending new page notification: {0}", ex);
        }
      }
#pragma warning disable FunctionNeverReturns
    }
#pragma warning restore FunctionNeverReturns
  }
}